#include "MapGenerator.h"
#include "MapPathCreator.h"
#include "Vector2D.h"


#pragma warning(disable:4018) // might want to remove this later


vector<GFXID>				MapGenerator::mGFXID;
vector<MapGenerator::Map>	MapGenerator::mMaps;
int							MapGenerator::mActiveMap = 0;

MapGenerator* MapGenerator::Instance()
{
	static MapGenerator instance;
	return &instance;
}

void CheckRange(Range &range)
{
	// return if we don't need to swap, otherwise swap
	if(range.max > range.min)
		return;

	Swap(range.max, range.min);
}
void MakeRangePositive(Range &range)
{
	range.max = abs(range.max);
	range.min = abs(range.min);
}

void MapGenerator::ChangeRoom(const Room& room)
{
	mMaps[mActiveMap].rooms[room.ID] = room;
}

int	MapGenerator::AddRoom (Range x1, Range y1, Range x2, Range y2, double density, int imgID, int algorithm)
{
	Swap(x1, y1);
	Swap(x2, y2);

	// make sure all args are positive
	density = abs(density);
	imgID = abs(imgID);
	algorithm = abs(algorithm);


	// Create a new room and fill it with blank data
	Room room = CreateRoom(x1, y1, x2, y2);


	switch (algorithm)
	{
	case SNAKE_FULL:
		SnakeFullAlgorithm(room, density, imgID);
		break;
	case SNAKE_FAST:
		SnakeFastAlgorithm(room, density, imgID);
		break;
	case CHISEL:
		ChiselAlgorithm(room, int(density * 40), imgID);
		break;
	case SCATTER:
		SparseAlgorithm(room, density, imgID);
		break;
	case BOX:
		BoxAlgorithm(room, density, imgID);
		break;
	case FOCUS_POINT:
		FocusPointAlgorithm(room, density, imgID);
		break;
	default:
		SnakeFastAlgorithm(room, density, imgID);
		break;
	}

	#ifdef CONSOLE_DEBUG
	mMaps[mActiveMap].totalRoomsGenerated++;
	#endif

	
	// Now that our room has been created, we need to add it to the map and set its ID
	room.ID = mMaps[mActiveMap].rooms.size();
	mMaps[mActiveMap].rooms.push_back(room);
	
	return mMaps[mActiveMap].rooms.size() - 1;
}

int MapGenerator::AddRoom (Range x1, Range y1, Range x2, Range y2, double density, int imgID, string algorithm)
{
	if(algorithm == "SNAKE_FULL")
		return AddRoom(x1, y1, x2, y2, density, imgID, SNAKE_FULL);
	if(algorithm == "SNAKE_FAST")
		return AddRoom(x1, y1, x2, y2, density, imgID, SNAKE_FAST);
	if(algorithm == "CHISEL")
		return AddRoom(x1, y1, x2, y2, density, imgID, CHISEL);
	if(algorithm == "SCATTER")
		return AddRoom(x1, y1, x2, y2, density, imgID, SCATTER);
	if(algorithm == "BOX")
		return AddRoom(x1, y1, x2, y2, density, imgID, BOX);
	if(algorithm == "FOCUS_POINT")
		return AddRoom(x1, y1, x2, y2, density, imgID, FOCUS_POINT);

	// if the string was invalid, return -1
	Log->AddMessage("ERROR: Bad algorithm passed to AddRoom");
	return -1;
}

int MapGenerator::MirrorRoom(int roomID, bool mirrorX, bool mirrorY)
{
	Log->AddMessage("MirrorRoom called");

	// basic error handling
	if(roomID < 0 || roomID > mMaps[mActiveMap].rooms.size())
	{
		Log->AddMessage("ERROR: Bad roomID passed to MirrorRoom");
		return -1;
	}

	Room orig = mMaps[mActiveMap].rooms[roomID];
	Room newRoom = orig;

	int mapHeight = mMaps[mActiveMap].mapHeight;
	int mapWidth = mMaps[mActiveMap].mapWidth;

	// switch x coords
	if(mirrorX)
	{
		newRoom.x1 = mapWidth - newRoom.x1;
		newRoom.x2 = mapWidth - newRoom.x2;	
	}

	// switch y coords
	if(mirrorY)
	{
		newRoom.y1 = mapHeight - newRoom.y1;
		newRoom.y2 = mapHeight - newRoom.y2;
	}

	// correct the location
	newRoom.EnsureDataIntegrity(); 

	// swap tiles
	for(int h = 0; h < newRoom.h; ++h)
		for(int w = 0; w < newRoom.w; ++w)
		{
			if(newRoom.w - w < 0)
				continue;
			if(newRoom.h - h < 0)
				continue;
			if(h >= newRoom.h)
				continue;
			if(w >= newRoom.w)
				continue;

			if(mirrorX && !mirrorY)
				newRoom.tiles[h][w][0] = orig.tiles[h][(newRoom.w - 1) - w][0];
			if(!mirrorX && mirrorY)
				newRoom.tiles[h][w][0] = orig.tiles[(newRoom.h -1) - h][w][0];
			if(mirrorX && mirrorY)
				newRoom.tiles[h][w][0] = orig.tiles[(newRoom.h - 1) - h][(newRoom.w - 1) - w][0];
		}

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "MirrorRoom" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < newRoom.h; ++h)
	{
		for(int w = 0; w < newRoom.w; ++w)
		{
			if(newRoom.tiles[h][w][0] == ignored)
				cout << " ";
			else
				cout << newRoom.tiles[h][w][0];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif

	Log->AddMessage("MirrorRoom finished");

	mMaps[mActiveMap].rooms.push_back(newRoom);
	return mMaps[mActiveMap].rooms.size() - 1;
}

int MapGenerator::AddPath(int startRoomID, int endRoomID, int pathWidth, int imageID, double scatterDensity, bool isRiver)
{
	Log->AddMessage("AddPath called");

	// simple error checks
	// if the rooms are invalid then return
	if(startRoomID < 0 || startRoomID > mMaps[mActiveMap].rooms.size())
	{
		Log->AddMessage("ERROR: Bad startRoomID passed to AddPath");
		return -1;
	}
	if(endRoomID < 0 || endRoomID > mMaps[mActiveMap].rooms.size())
	{
		Log->AddMessage("ERROR: Bad endRoomID passed to AddPath");
		return -1;
	}
	if(!mMaps[mActiveMap].NavGraphCreated)
	{
		Log->AddMessage("ERROR: NavGraph was not created when AddPath was called");
		return -1;
	}

	Vector2D startPos, endPos;
	// create our new room
	Room startRoom = mMaps[mActiveMap].rooms[startRoomID];
	Room endRoom = mMaps[mActiveMap].rooms[endRoomID];
	int ignored = ImageManager->GetImageID("ignored_tile");

	if(startRoom.w <= 0 || startRoom.h <= 0 || endRoom.w <= 0 || endRoom.h <= 0)
	{
		Log->AddMessage("ERROR: AddPath recieved a room with a dimension of 0");
		return -1;
	}
	
	// find our locations
	{
		Coords at = Coords(0,0);
		bool stop = false;
		int counter = startRoom.w * startRoom.h * 5;

		while(!stop && counter > 0)
		{
			at.x = Rand(0, startRoom.w - 1);
			at.y = Rand(0, startRoom.h - 1);
			if(startRoom.tiles[at.y][at.x][0] != ignored)
				stop = true;

			--counter;
		}
		startPos.x = startRoom.x1 + (at.x);
		startPos.y = startRoom.y1 + (at.y);


		stop = false;
		counter = endRoom.w * endRoom.h * 5;
		int grass = ImageManager->GetImageID("grass");
		while(!stop && counter > 0)
		{
			at.x = Rand(0, endRoom.w - 1);
			at.y = Rand(0, endRoom.h - 1);
			if(endRoom.tiles[at.y][at.x][0] == grass)
				stop = true;

			--counter;
		}
		endPos.x = endRoom.x1 + (at.x);
		endPos.y = endRoom.y1 + (at.y);

		// error handling
		if(startPos.x >= DataLoader->GetMapWidth())
			startPos.x = DataLoader->GetMapWidth() - 1;
		if(startPos.y >= DataLoader->GetMapHeight())
			startPos.y = DataLoader->GetMapHeight() - 1;
		if(startPos.x < 0)
			startPos.x = 0;
		if(startPos.y < 0)
			startPos.y = 0;
	}

	

	Path* path = NULL;
	if(scatterDensity > 0)
	{
		// set the variation levels
		HeuristicNoisyEuclidianVariation.max = scatterDensity;
		path = mMaps[mActiveMap].pathCreator->GeneratePath(startPos * TILE_DIMENSIONS, endPos * TILE_DIMENSIONS, true); // vector 
	}
	else
		path = mMaps[mActiveMap].pathCreator->GeneratePath(startPos * TILE_DIMENSIONS, endPos * TILE_DIMENSIONS);


	list<Vector2D> path2 = path->GetWaypoints();
	list<Vector2D>::iterator iter;

	NullDelete(path);
	Log->AddMessage("AddPath is now figuring out the room dimensions");

	// room dimensions
	
	int x1, y1, x2, y2;
	x1 = startPos.x * TILE_DIMENSIONS;
	x2 = endPos.x * TILE_DIMENSIONS;
	y1 = startPos.y * TILE_DIMENSIONS;
	y2 = endPos.y * TILE_DIMENSIONS;

	// get the room dimensions
	for(iter = path2.begin(); iter != path2.end(); ++iter)
	{
		// if our current loc is greater than our current pos
		if(x1 > iter->x)
			x1 = iter->x;

		if(x2 < iter->x)
			x2 = iter->x;
		
		if(y1 > iter->y)
			y1 = iter->y;

		if(y2 < iter->y)
			y2 = iter->y;
	}
	
	// put tile location in grid coordinates
	x1 /= TILE_DIMENSIONS;
	x2 /= TILE_DIMENSIONS;
	y1 /= TILE_DIMENSIONS;
	y2 /= TILE_DIMENSIONS;

	// round x1 / y1 up, not down
	x1 += 1;
	y1 += 1;

	// adjusts location to make sure to fit the entire path
	x1 -= pathWidth;
	y1 -= pathWidth;
	x2 += pathWidth;
	y2 += pathWidth;

	if(x1 < 0)
		x1 = 0;
	if(x1 > mMaps[mActiveMap].mapWidth)
		x1 = mMaps[mActiveMap].mapWidth;

	if(x2 <= 0)
		x2 = 0;
	if(x2 > mMaps[mActiveMap].mapWidth)
		x2 = mMaps[mActiveMap].mapWidth;

	if(y1 < 0)
		y1 = 0;
	if(y1 > mMaps[mActiveMap].mapHeight)
		y1 = mMaps[mActiveMap].mapHeight;

	if(y2 <= 0)
		y2 = 0;
	if(y2 > mMaps[mActiveMap].mapHeight)
		y2 = mMaps[mActiveMap].mapHeight;

	// create our new room
	Room room = CreateRoom(x1, y1, x2, y2);
	room.gfxID = imageID;
	room.isPath = true;
	room.isRiver = isRiver;
	
	// place our room
	Log->AddMessage("AddPath is now laying down the path");
	Coords at = Coords(0,0);
	for(iter = path2.begin(); iter != path2.end(); ++iter)
	{
		at.x = iter->x / TILE_DIMENSIONS;
		at.y = iter->y / TILE_DIMENSIONS;

		if(at.x > 0)
			at.x -= room.x1;
		if(at.y > 0)
			at.y -= room.y1;

		if(at.x <= 0)
			at.x *= -1;
		if(at.y <= 0)
			at.y *= -1;

		int h = room.h;
		int w = room.w;
		if((at.x >= w) || (at.y >= h) || (at.x < 0) || (at.y < 0))
			continue;

		room.tiles[at.y][at.x][0] = imageID;

		//cout << "room w: " << newRoom.w << "\nroom h: " << newRoom.h << endl;
		for(int i = -(pathWidth / 2.0); i < (pathWidth / 2.0); ++i)
		{
			//cout << "at.y + i: " << at.y + i << "\nat.x + i " << at.x + i << endl;
			if(((at.y + i) < room.h) && ((at.y + i) >= 0))
				room.tiles[at.y + i][at.x][0] = imageID;
			if(((at.x + i) < room.w) && ((at.x + i) >= 0))
				room.tiles[at.y][at.x + i][0] = imageID;
		}
	}

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << "snow: " << ImageManager->GetImageID("snow");
	cout << "insert: " << imageID;
	cout << endl << "PATH" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < newRoom.h; ++h)
	{
		for(int w = 0; w < newRoom.w; ++w)
		{
			if(newRoom.tiles[h][w][0] == ignored)
				cout << " ";
			else
				cout << newRoom.tiles[h][w][0];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif

	Log->AddMessage("AddPath completed");

	room.ID = mMaps[mActiveMap].rooms.size();
	mMaps[mActiveMap].rooms.push_back(room);
	return mMaps[mActiveMap].rooms.size() - 1;
}

Room MapGenerator::CreateRoom(int x1, int y1, int x2, int y2, int imgID)
{
	if(imgID == -1)
		imgID = ImageManager->GetImageID("ignored_tile");

	return CreateRoom(GetRange(x1, x1), GetRange(y1, y1), GetRange(x2, x2), GetRange(y2, y2), imgID);
}
Room MapGenerator::CreateRoom(Range x1, Range y1, Range x2, Range y2, int imgID)
{

	// make sure all ranges are positive
	MakeRangePositive(x1);
	MakeRangePositive(y1);
	MakeRangePositive(x2);
	MakeRangePositive(y2);

	// switch ranges if necessary
	CheckRange(x1);
	CheckRange(y1);
	CheckRange(x2);
	CheckRange(y2);

	// make sure the ranges are in the map's boundaries
	// min
	if(x1.min > DataLoader->GetMapWidth())
		x1.min = DataLoader->GetMapWidth();
	if(x2.min > DataLoader->GetMapWidth())
		x2.min = DataLoader->GetMapWidth();
	if(y1.min > DataLoader->GetMapHeight())
		y1.min = DataLoader->GetMapHeight();
	if(y2.min > DataLoader->GetMapHeight())
		y2.min = DataLoader->GetMapHeight();

	// max
	if(x1.max > DataLoader->GetMapWidth())
		x1.max = DataLoader->GetMapWidth();
	if(x2.max > DataLoader->GetMapWidth())
		x2.max = DataLoader->GetMapWidth();
	if(y1.max > DataLoader->GetMapHeight())
		y1.max = DataLoader->GetMapHeight();
	if(y2.max > DataLoader->GetMapHeight())
		y2.max = DataLoader->GetMapHeight();

	// Create a new room and fill out the variables
	Room room;
	room.x1 = Rand(x1);
	room.y1 = Rand(y1);
	room.x2 = Rand(x2);
	room.y2 = Rand(y2);
	room.w = (room.x2 > room.x1) ? (room.x2 - room.x1) : (room.x1 - room.x2);
	room.h = (room.y2 > room.y1) ? (room.y2 - room.y1) : (room.y1 - room.y2);

	// Make sure the random check was in bounds
	if(room.x1 > x1.max)
		room.x1 = x1.max;
	if(room.x1 < x1.min)
		room.x1 = x1.min;
	if(room.x2 > x2.max)
		room.x2 = x2.max;
	if(room.x2 < x2.min)
		room.x2 = x2.min;
	if(room.y1 > y1.max)
		room.y1 = y1.max;
	if(room.y1 < y1.min)
		room.y1 = y1.min;
	if(room.y2 > y2.max)
		room.y2 = y2.max;
	if(room.y2 < y2.min)
		room.y2 = y2.min;


	// Fill the room with all ignored tiles
	int ignored = ImageManager->GetImageID("ignored_tile");

	room.tiles.resize(room.h);			// Add height to the room
	for(int h = 0; h < room.h; ++h)
	{
		room.tiles[h].resize(room.w);		// Add width to the room

		for(int w = 0; w < room.w; ++w)
			room.tiles[h][w].push_back(imgID);	// Go through every room tile and set it to be ignored
	}
	
	

	// yes, this room has been created
	room.HasBeenCreated = true;
	room.gfxID = imgID;
	room.EnsureDataIntegrity();

	// give the user their room
	return room;
}

void MapGenerator::BoxAlgorithm(Room& room, double density, int imgID)
{	
	// Make sure the room is big enough
	assert("Room has not been created in Box Algorithm" && room.HasBeenCreated);

	Log->AddMessage("BoxAlgorithm called");
	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");


	int prevG = room.gfxID;

	// fill the entire room with data
	room = CreateRoom(room.x1, room.y1, room.x2, room.y2, imgID);

	room.gfxID = prevG;
	
	ChiselAlgorithm(room, int(density * 40), IGNORED_TILE);

	Log->AddMessage("BoxAlgorithm completed");

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "BOX" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < room.h; ++h)
	{
		for(int w = 0; w < room.w; ++w)
		{
			if(room.tiles[h][w][0] == ImageManager->GetImageID("ignored_tile"))
				cout << " ";
			else
				cout << room.tileData[h][w][0];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif

}

void MapGenerator::SparseAlgorithm(Room& room, double density, int imgID)
{
	// Make sure the room is big enough
	assert("Room has not been created in Sparse Algorithm" && room.HasBeenCreated);
	Log->AddMessage("SparseAlgorithm called");

	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");

	Coords at;
	int occupied = 0;

	
	// while we still have nodes to place
	while (occupied <= (density * room.h * room.w))
	{
		
		// Find a random location to place the tile
		at.y = Rand(0, room.h - 1);
		at.x = Rand(0, room.w - 1);	


		// if we are at a real location and the location is ignored then place the tile
		if((at.y < room.h - 1) && (at.y >= 0)) 
		{
			if (( at.x < room.w) && (at.x >= 0))
				if(room.tiles[at.y][at.x][0] == IGNORED_TILE)
				{
					room.tiles[at.y][at.x][0] = imgID; // add the tile
					++occupied;
				}
		}		
	}

	Log->AddMessage("SparseAlgorithm completed");

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "SPARSE" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < room.h; ++h)
	{
		for(int w = 0; w < room.w; ++w)
		{
			if(room.tileData[layer][h][w] == 2)
				cout << " ";
			else
				cout << room.tileData[layer][h][w];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif

}

void MapGenerator::SnakeFullAlgorithm(Room& room, double density, int imgID)
{
	assert("Room has not been created in Snake Full Algorithm" && room.HasBeenCreated);
	Log->AddMessage("SnakeFullAlgorithm called");
	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");

	Coords at = Coords(-1, -1);
	int occupied = 0;

	vector<Coords> nodes;
	enum Directions { up, down, left, right };

	// place a tile at a random location
	while(at.y != abs(at.y) || at.y > room.h - 1)
		at.y = Rand(0, room.h - 1);
	while(at.x != abs(at.x) || at.x > room.w - 1)
		at.x = Rand(0, room.w - 1);
	room.tiles[at.y][at.x][0] = imgID; // add the tile // was ==
	nodes.push_back(at); // add the tile to our list of nodes
	++occupied;

	int totalNodes = (density * room.w * room.h);
	if(totalNodes <= 0)
		totalNodes = 1;

	// while we still have nodes to place
	while (occupied <= totalNodes)
	{
		// move
		int dir = Rand(up, right);

		if (dir == up)
			--at.y;
		else if (dir == down)
			++at.y;
		else if (dir == left)
			--at.x;
		else if (dir == right)
			++at.x;

		bool repick = false;

		// if we are at a real location and the location is ignored then place the tile
		if((at.y <= room.h - 1) && (at.y >= 0)) 
		{
			if (( at.x < room.w) && (at.x >= 0))
				if(room.tiles[at.y][at.x][0] == IGNORED_TILE)
				{
					room.tiles[at.y][at.x][0] = imgID; // add the tile
					nodes.push_back(at); // add the tile to our list of nodes
					++occupied;
				}
				else
					repick = true;
			else
				repick = true;
		}
		else
			repick = true;
		// otherwise we need to generate a new location
		if(repick)
		{
			if(nodes.size() == 0)
				assert("nodes.size() in snake algorithm is zero" && false);

			int loc = Rand(0, nodes.size() - 1);

			if(loc < 0 || loc > nodes.size() - 1)
				assert("Invalid new random node location" && false);

			at = nodes[loc];	

		}
	}

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "SNAKE FULL" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < room.h; ++h)
	{
		for(int w = 0; w < room.w; ++w)
		{
			if(room.tileData[layer][h][w] == 2)
				cout << " ";
			else
				cout << room.tileData[layer][h][w];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif

	ChiselAlgorithm(room, density * 40, IGNORED_TILE);

	Log->AddMessage("SnakeFullAlgorithm completed");
}

void MapGenerator::SnakeFastAlgorithm(Room& room, double density, int imgID)
{
	Log->AddMessage("SnakeFastAlgorithm called");

	if(room.h == 0 || room.w == 0)
	{
		Log->AddMessage("ERROR: SnakeFastAlgorithm recieved a room with either zero width or zero height");
		return;
	}

	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");
	
	Coords at = Vector2D(-1, -1);
	int occupied = 0;

	vector<Coords> nodes;
	enum Directions { up, down, left, right };

	// place a tile at a random location
	while(at.y != abs(at.y) || at.y > room.h - 1)
		at.y = Rand(0, room.h - 1);
	while(at.x != abs(at.x) || at.x > room.w - 1)
		at.x = Rand(0, room.w - 1);
	room.tiles[at.y][at.x][0] = imgID; // add the tile // was ==
	nodes.push_back(at); // add the tile to our list of nodes
	++occupied;
	
	int totalNodes = (density * room.w * room.h);
	if(totalNodes <= 0)
		totalNodes = 1;

	// while we still have nodes to place
	while (occupied <= totalNodes)
	{
		// move
		int dir = Rand(up, right);

		if (dir == up)
			--at.y;
		else if (dir == down)
			++at.y;
		else if (dir == left)
			--at.x;
		else if (dir == right)
			++at.x;

		bool repick = false;

		// if we are at a real location and the location is ignored then place the tile
		if((at.y <= room.h - 1) && (at.y >= 0)) 
		{
			if (( at.x < room.w) && (at.x >= 0))
				//if(room.tileData[layer][at.y][at.x] == IGNORED_TILE)
				{
					room.tiles[at.y][at.x][0] = imgID; // add the tile
					nodes.push_back(at); // add the tile to our list of nodes
					++occupied;
				}
				//else
				//	repick = true;
			else
				repick = true;
		}
		else
			repick = true;
		// otherwise we need to generate a new location
		if(repick)
		{
			if(nodes.size() == 0)
				assert("nodes.size() in snake algorithm is zero" && false);

			int loc = Rand(0, nodes.size() - 1);

			if(loc < 0 || loc > nodes.size() - 1)
				assert("Invalid new random node location" && false);

			at = nodes[loc];	

		}
	}

	Log->AddMessage("SnakeFastAlgorithm completed");

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "SNAKE FAST" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < room.h; ++h)
	{
		for(int w = 0; w < room.w; ++w)
		{
			if(room.tileData[layer][h][w] == 2)
				cout << " ";
			else
				cout << room.tileData[layer][h][w];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif
}

void MapGenerator::ChiselAlgorithm(Room& room, int variation, int imgID)
{
	Log->AddMessage("ChiselAlgorithm called");
	if(room.h == 0 || room.w == 0)
	{
		Log->AddMessage("ERROR: ChiselAlgorithm recieved a room with height or width of zero");
		return;
	}

	int height = room.h - 1;
	int width = room.w - 1;
	

	// Left & Right
	for(int h = 0; h < height; ++h)
	{
		// Left Side
		//	(first indent)
		if(Rand(1, variation) > VARIATION_REMOVE)
			room.tiles[h][0][0] = imgID;

		// (second indent)
		if(Rand(1, variation / 2) > VARIATION_REMOVE && width > 1)
			room.tiles[h][1][0] = imgID;
		// (third  indent)
		if(Rand(1, variation / 4) > VARIATION_REMOVE && width > 2)
			room.tiles[h][2][0] = imgID;

		// Right Side
		//	(first ident)
		if(Rand(1, variation) > VARIATION_REMOVE)
			room.tiles[h][width][0] = imgID;

		//	(second ident)
		if(Rand(1, variation / 2) > VARIATION_REMOVE && width > 0)
			room.tiles[h][width - 1][0] = imgID;

		//	(third ident)
		if(Rand(1, variation / 4) > VARIATION_REMOVE && width > 1)
			room.tiles[h][width - 2][0] = imgID;
	}

	// Top & Bottom
	for(int w = 0; w < width; ++w)
	{
		// Top
		//	(first indent)
		if(Rand(1, variation) > VARIATION_REMOVE)
			room.tiles[height][w][0] = imgID;

		//	(second indent)
		if(Rand(1, variation / 2) > VARIATION_REMOVE && height > 0)
			room.tiles[height - 1][w][0] = imgID;

		//	(third indent)
		if(Rand(1, variation / 4) > VARIATION_REMOVE && height > 1)
			room.tiles[height - 2][w][0] = imgID;

		// Bottom
		//	(first indent)
		if(Rand(1, variation) > VARIATION_REMOVE)
			room.tiles[0][w][0] = imgID;

		//	(second indent)
		if(Rand(1, variation / 2) > VARIATION_REMOVE && height > 1)
			room.tiles[1][w][0] = imgID;

		//	(third indent)
		if(Rand(1, variation / 4) > VARIATION_REMOVE && height > 2)
			room.tiles[2][w][0] = imgID;

	}

	Log->AddMessage("ChiselAlgorithm completed");

	// Display our room
	#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
	cout << endl << "CHISEL" << endl;
	cout << "------------------------------------------------" << endl;
	for(int h = 0; h < room.h; ++h)
	{
		for(int w = 0; w < room.w; ++w)
		{
			if(room.tileData[h][w][0] == 2)
				cout << " ";
			else
				cout << room.tileData[h][w][0];
		}
		cout << endl;
	}
	cout << "------------------------------------------------";
	#endif
}

void MapGenerator::SaveMap(const char *path)
{
	Log->AddMessage("MapGenerator saving map at " + GetString(path));

	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");
	int height = mMaps[mActiveMap].mapHeight;
	int width = mMaps[mActiveMap].mapWidth;
	#ifdef CONSOLE_DEBUG
	float time = mMaps[mActiveMap].timer.GetElapsedSeconds() / 1000.0f;
	mMaps[mActiveMap].timer.Restart();
	#endif
	
	// Create a new map
	vector<vector<vector<int>>> map;
	// HEIGHT WIDTH LAYER
	map.resize(height + 1);
	for(int h = 0; h < height + 1; ++h)
		map[h].resize(width + 1);
	
	// Post process our rooms
	Log->AddMessage("PostProcessing map");
	for(int i = 1; i < mMaps[mActiveMap].rooms.size(); ++i) // start at 1 because we don't postprocess the "background"
		PostProcessRoom(mMaps[mActiveMap].rooms[i]);

	// Place the "world" first
	Log->AddMessage("Placing the \"world\"");
	for(int h = 0; h < mMaps[mActiveMap].rooms[0].h; ++h)
		for(int w = 0; w < mMaps[mActiveMap].rooms[0].w; ++w)
			map[h][w].push_back(mMaps[mActiveMap].rooms[0].tiles[h][w][0]);
	
	//for(int w = 0; w < width; ++w)
	//	map[height][w].push_back(mMaps[mActiveMap].rooms[0].tiles[0][0][0]);

	// General Paths first
	Log->AddMessage("Placing general paths");
	for(int i = 1; i < mMaps[mActiveMap].rooms.size(); ++i) // start at 1 because base is 0
		if(mMaps[mActiveMap].rooms[i].isPath == true && mMaps[mActiveMap].rooms[i].isRiver == false)
		{
			for(int h = 0; h < mMaps[mActiveMap].rooms[i].h; ++h)
				for(int w = 0; w < mMaps[mActiveMap].rooms[i].w; ++w)
				{
					int locX = w + mMaps[mActiveMap].rooms[i].x1;
					int locY = h + mMaps[mActiveMap].rooms[i].y1;
					if(locX < 0 || locY < 0 || locY >= map.size() - 1 || locX >= map[locY].size() - 1)
						continue;			
					
					// place everything else wherever available
					for(int q = 0; q < mMaps[mActiveMap].rooms[i].tiles[h][w].size(); ++q)
						if((mMaps[mActiveMap].rooms[i].tiles[h][w][q] != IGNORED_TILE))
						{
							if(q == 0)
								map[locX][locY][0] = (mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
							
							map[locX][locY].push_back(mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
						}
				}
		}


	// General stuff in the middle
	Log->AddMessage("Placing rooms");
	for(int i = 1; i < mMaps[mActiveMap].rooms.size(); ++i) // start at 1 because base is 0
		if(mMaps[mActiveMap].rooms[i].isPath == false && mMaps[mActiveMap].rooms[i].isRiver == false)
		{
			for(int h = 0; h < mMaps[mActiveMap].rooms[i].h; ++h)
				for(int w = 0; w < mMaps[mActiveMap].rooms[i].w; ++w)
				{
					int locX = w + mMaps[mActiveMap].rooms[i].x1;
					int locY = h + mMaps[mActiveMap].rooms[i].y1;
					if(locX < 0 || locY < 0 || locY >= map.size() - 1 || locX >= map[locY].size() - 1)
						continue;			
					
					// place everything else wherever available
					for(int q = 0; q < mMaps[mActiveMap].rooms[i].tiles[h][w].size(); ++q)
						if((mMaps[mActiveMap].rooms[i].tiles[h][w][q] != IGNORED_TILE))
						{
							if(q == 0)
								map[locX][locY][0] = (mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
							
							map[locX][locY].push_back(mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
						}
							
				}
		}


	// Rivers last
	Log->AddMessage("Placing \"rivers\"");
	for(int i = 1; i < mMaps[mActiveMap].rooms.size(); ++i) // start at 1 because base is 0
		if(mMaps[mActiveMap].rooms[i].isPath == true && mMaps[mActiveMap].rooms[i].isRiver == true)
		{
			for(int h = 0; h < mMaps[mActiveMap].rooms[i].h; ++h)
				for(int w = 0; w < mMaps[mActiveMap].rooms[i].w; ++w)
				{
					int locX = w + mMaps[mActiveMap].rooms[i].x1;
					int locY = h + mMaps[mActiveMap].rooms[i].y1;
					if(locX < 0 || locY < 0 || locY >= map.size() - 1 || locX >= map[locY].size() - 1)
						continue;			
					
					// place everything else wherever available
					for(int q = 0; q < mMaps[mActiveMap].rooms[i].tiles[h][w].size(); ++q)
						if((mMaps[mActiveMap].rooms[i].tiles[h][w][q] != IGNORED_TILE))
						{
							if(q == 0)
								map[locX][locY][0] = (mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
							
							map[locX][locY].push_back(mMaps[mActiveMap].rooms[i].tiles[h][w][q]);
						}
							
				}
		}
	//map[99][99].push_back(ImageManager->GetImageID("dirt"));
	//map[99][100].push_back(ImageManager->GetImageID("dirt"));
	//map[100][100].push_back(ImageManager->GetImageID("dirt"));
	/*int dirt = ImageManager->GetImageID("grass");
	map[0][11][12] = dirt;	map[0][11][13] = dirt;	map[0][11][14] = dirt;	map[0][11][15] = dirt;	map[0][11][16] = dirt;
	map[0][12][12] = dirt;	map[0][12][13] = dirt;	map[0][12][14] = dirt;	map[0][12][15] = dirt;	map[0][12][16] = dirt;
	map[0][13][12] = dirt;	map[0][13][13] = dirt;
							map[0][14][13] = dirt;
							map[0][15][13] = dirt;
	map[0][16][12] = dirt;	map[0][16][13] = dirt;	map[0][16][14] = dirt;
							map[0][17][13] = dirt;*/

	/*map[0][11][12] = 40;							map[0][11][14] = 40;
	map[0][12][12] = 40;							map[0][12][14] = 40;
	map[0][13][12] = 40;							map[0][13][14] = 40;
	map[0][14][12] = 40;							map[0][14][14] = 40;*/

	// Perform post processing
	//PostProcess(map);

    // Save the map
	// Open a file that we can write to
    ofstream save_file;
    save_file.open(path);

    if(save_file.is_open())
    {
		Log->AddMessage("Saving map");
		save_file << mMaps[mActiveMap].mapWidth << " " << mMaps[mActiveMap].mapHeight << "\n";

		for(int y = 0; y < mMaps[mActiveMap].mapHeight; ++y)
		{
			for(int x = 0; x < mMaps[mActiveMap].mapWidth; ++x)
			{
				for(int i = 0; i < map[y][x].size(); ++i) // total layers
					save_file << map[y][x][i] << " ";

				save_file << " -> ";

				#ifdef MAKE_MAP_GENERATOR_SUPER_WORDY
				cout << map[0][y][x];
				#endif
			}
			save_file << "\n";
		}

		save_file.close();
	}
	else
		Log->AddMessage("Error: Save file is not open");

	Log->AddMessage("Deleting nav graph");
	mMaps[mActiveMap].DeleteNavGraph();

	#ifdef CONSOLE_DEBUG
	cout << endl << "Total map generation time: " << time + (mMaps[mActiveMap].timer.GetElapsedSeconds() / 1000.0f) << " seconds." << endl;
	cout << "Save map time: " << time << " seconds." << endl;
	cout << "Total rooms generated: " << mMaps[mActiveMap].totalRoomsGenerated << "." << endl;
	#endif

	Log->AddMessage("Save map is complete");
}


int MapGenerator::CreateMap(int gfxID, int width, int height)
{
	Map test;
	mMaps.push_back(test);
	mMaps[mMaps.size() - 1].CreateNavGraph();

	Room totalWorld = CreateRoom(0, 0, width, height, gfxID);
	totalWorld.gfxID = gfxID;

	mMaps[mMaps.size() - 1].rooms.push_back(totalWorld);
	mMaps[mMaps.size() - 1].mapHeight = height;
	mMaps[mMaps.size() - 1].mapWidth = width;
	mMaps[mMaps.size() - 1].baseID = gfxID;
	#ifdef CONSOLE_DEBUG
	mMaps[mMaps.size() - 1].timer.Restart();
	mMaps[mMaps.size() - 1].totalRoomsGenerated = 0;
	#endif

	Log->AddMessage("MapGenerator has created a new map");

	return mMaps.size() - 1;
}


void	MapGenerator::AddGFXID	(string base, string topLeft, string bottomLeft, string bottomRight,
						string topRight, string left, string bottom, string right, string top)
{

	GFXID gfx;
	
	gfx.base	=	ImageManager->GetImageID(base); 

	gfx.right	=	ImageManager->GetImageID(right);
	gfx.left	=	ImageManager->GetImageID(left);
	gfx.top		=	ImageManager->GetImageID(top);
	gfx.bottom	=	ImageManager->GetImageID(bottom);

	gfx.bottomRight	=	ImageManager->GetImageID(bottomRight);
	gfx.bottomLeft	=	ImageManager->GetImageID(bottomLeft);
	gfx.topRight	=	ImageManager->GetImageID(topRight);
	gfx.topLeft		=	ImageManager->GetImageID(topLeft);

	mGFXID.push_back(gfx);
}

/* ROOM VERSION */
void ProcessTile(vector<vector<vector<int>>> &world, Dimension& at, GFXID gfxID, const int& IGNORED_TILE)
{
	int w = at.w, h = at.h;
	
	if(gfxID.base == world[h][w][0])
	{
		// We found our gfxID, now lets go around the tile and examine what is around it
		//int orig = world[h][w][layer];	// We need to know what tile we are working with
		int orig = gfxID.base;

		// LEFT TILE goes up
		// If the left tile is not the same as the original tile then we need to apply
		if((w > 0) && (world[h][w-1][0] != orig))
			world[h][w-1].push_back(gfxID.top);
		
		// TOP TILE problem one
		if((h > 0) && (world[h-1][w][0] != orig))
			world[h-1][w].push_back(gfxID.left);

		// RIGHT TILE problem one
		if((w < world[h].size() - 1) && (world[h][w+1][0] != orig))
		{
			world[h][w+1].push_back(gfxID.bottom);
		}

		// BOTTOM TILE goes right
		if((h < world.size() - 1) && (world[h+1][w][0] != orig))
			world[h+1][w].push_back(gfxID.right);


		// TOP RIGHT problem one
		if((h > 0) && (w < world[h].size() - 1) && (world[h-1][w][0] != orig) && (world[h][w+1][0] != orig))
			world[h-1][w+1].push_back(gfxID.topRight);

		// TOP LEFT
		if((h > 0) && (w > 0) && (world[h-1][w][0] != orig) && (world[h][w-1][0] != orig))
			world[h-1][w-1].push_back(gfxID.bottomRight);
				

		// BOTTOM RIGHT
		if((h < world.size() - 1) && (w < world[h].size() - 1) && (world[h+1][w][0] != orig) && (world[h][w+1][0] != orig))
			world[h+1][w+1].push_back(gfxID.topLeft);

		// BOTTOM LEFT
		if((h < world.size() - 1) && (w > 0) && (world[h+1][w][0] != orig) && (world[h][w-1][0] != orig))
			world[h+1][w-1].push_back(gfxID.bottomLeft);
	}
}

void ProcessTileCorrect(vector<vector<vector<int>>> &world, Dimension& at, GFXID gfxID, const int& IGNORED_TILE)
{
	int w = at.w, h = at.h;
	
	if(gfxID.base == world[h][w][0])
	{
		// We found our gfxID, now lets go around the tile and examine what is around it
		//int orig = world[h][w][layer];	// We need to know what tile we are working with
		int orig = gfxID.base;

		// LEFT TILE goes up
		// If the left tile is not the same as the original tile then we need to apply
		if((w > 0) && (world[h][w-1][0] != orig))
			world[h][w-1].push_back(gfxID.left);
		
		// TOP TILE problem one
		if((h > 0) && (world[h-1][w][0] != orig))
			world[h-1][w].push_back(gfxID.top);

		// RIGHT TILE problem one
		if((w < world[h].size() - 1) && (world[h][w+1][0] != orig))
		{
			world[h][w+1].push_back(gfxID.right);
		}

		// BOTTOM TILE goes right
		if((h < world.size() - 1) && (world[h+1][w][0] != orig))
			world[h+1][w].push_back(gfxID.bottom);


		// TOP RIGHT problem one
		if((h > 0) && (w < world[h].size() - 1) && (world[h-1][w][0] != orig) && (world[h][w+1][0] != orig))
			world[h-1][w+1].push_back(gfxID.bottomLeft);

		// TOP LEFT
		if((h > 0) && (w > 0) && (world[h-1][w][0] != orig) && (world[h][w-1][0] != orig))
			world[h-1][w-1].push_back(gfxID.bottomRight);
				

		// BOTTOM RIGHT
		if((h < world.size() - 1) && (w < world[h].size() - 1) && (world[h+1][w][0] != orig) && (world[h][w+1][0] != orig))
			world[h+1][w+1].push_back(gfxID.topLeft);

		// BOTTOM LEFT
		if((h < world.size() - 1) && (w > 0) && (world[h+1][w][0] != orig) && (world[h][w-1][0] != orig))
			world[h+1][w-1].push_back(gfxID.topRight);
	}
}

void MapGenerator::PostProcessRoomCorrectWay(Room& room)
{
	// create a new room with 2 units more space than the previous one
	vector<vector<vector<int> > > newRoom;
	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");

	int height = room.h + 2;
	int width = room.w + 2;

	newRoom.resize(height);
	for(int h = 0; h < height; ++h)
	{
		newRoom[h].resize(width);
		for(int w = 0; w < width; ++w)
			newRoom[h][w].push_back(IGNORED_TILE); // set everything to be ignored
	}

	// Add the room's data to the new room
		for(int h = 0; h < height; ++h)
			for(int w = 0; w < width; ++w)
			{
				//newRoom[l][h][w] = IGNORED_TILE; // set everything to be ignored

				// if we are in the bounds of the original tile data then set that
				if(h < room.tiles.size() && w < room.tiles[h].size()) // the minus one prevents a ____________ (snug right up to the corner)
				{
					//for(int l = 0; l < room.tiles[h][w].size(); ++l)
						//if(l == 0)
							newRoom[h+1][w+1][0] = room.tiles[h][w][0];
						
						//newRoom[h+1][w+1].push_back(room.tiles[h][w][l]);

				}
			}


	// Post process this room
	Dimension at = {0, 0};
	// Iterate through all the tiles in the world
		for(at.h = 0; at.h < height; ++at.h)
			for(at.w = 0; at.w < width; ++at.w)
				for(int i = 0; i < mGFXID.size(); ++i)
					if(mGFXID[i].base == newRoom[at.h][at.w][0])
						ProcessTileCorrect(newRoom, at, mGFXID[i], IGNORED_TILE);



	room.tiles.clear();
	// Transpose the tile data into the room data
	room.tiles = newRoom;
	room.w = width;
	room.h = height;
	room.x2 = room.x1 + room.w;
	room.y2 = room.y1 + room.h;
	
}

void MapGenerator::PostProcess(std::vector<vector<vector<int> > > &world)
{
	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");

	Dimension at;

	// Iterate through all the tiles in the world
	for(at.h = 0; at.h < world.size(); ++at.h)
		for(at.w = 0; at.w < world[at.h].size(); ++at.w)
			if(world[at.h][at.w][0] != IGNORED_TILE)
				if(world[at.h][at.w][0] != mMaps[mActiveMap].baseID)
					for(int i = 0; i < mGFXID.size(); ++i)
						if(mGFXID[i].base == world[at.h][at.w][0])
							ProcessTile(world, at, mGFXID[i], IGNORED_TILE);



}

void MapGenerator::PostProcessRoom(Room &room)
{
	// create a new room with 2 units more space than the previous one
	vector<vector<vector<int> > > newRoom;
	int IGNORED_TILE = ImageManager->GetImageID("ignored_tile");

	int height = room.h + 2;
	int width = room.w + 2;

	newRoom.resize(height);
	for(int h = 0; h < height; ++h)
	{
		newRoom[h].resize(width);
		for(int w = 0; w < width; ++w)
			newRoom[h][w].push_back(IGNORED_TILE); // set everything to be ignored
	}

	// Add the room's data to the new room
		for(int h = 0; h < height; ++h)
			for(int w = 0; w < width; ++w)
			{
				//newRoom[l][h][w] = IGNORED_TILE; // set everything to be ignored

				// if we are in the bounds of the original tile data then set that
				if(h < room.tiles.size() && w < room.tiles[h].size()) // the minus one prevents a ____________ (snug right up to the corner)
				{
					//for(int l = 0; l < room.tiles[h][w].size(); ++l)
						//if(l == 0)
							newRoom[h+1][w+1][0] = room.tiles[h][w][0];
						
						//newRoom[h+1][w+1].push_back(room.tiles[h][w][l]);

				}
			}


	// Post process this room
	Dimension at = {0, 0};
	// Iterate through all the tiles in the world
		for(at.h = 0; at.h < height; ++at.h)
			for(at.w = 0; at.w < width; ++at.w)
				for(int i = 0; i < mGFXID.size(); ++i)
					if(mGFXID[i].base == newRoom[at.h][at.w][0])
						ProcessTile(newRoom, at, mGFXID[i], IGNORED_TILE);



	room.tiles.clear();
	// Transpose the tile data into the room data
	room.tiles = newRoom;
	room.w = width;
	room.h = height;
	room.x2 = room.x1 + room.w;
	room.y2 = room.y1 + room.h;
		/*room.tiles.resize(height);			// Add height to the room
		for(int h = 0; h < height; ++h)
		{
			room.tiles[h].resize(width);		// Add width to the room

			for(int w = 0; w < width; ++w)
				for(int l = 0; l < newRoom[h][w].size(); ++l)
					room.tiles[h][w].push_back(newRoom[h][w][l]);	// Go through every room tile and set it to be ignored
		}*/
	


}








// Returns true / false if the location is not safe and will cause an error on the vector
bool SafeLocation(const vector<vector<int> >& map, int y, int x)
{
	if(y >= map.size() || y < 0)
		return false;
	if(x >= map[y].size() || x < 0)
		return false;

	return true;

}

bool FocusPointAliveOrDead(double baseKillPercentage, int survivalChance)
{
	// if the random number is higher than the base kill percentage of the survival chance, then return false
	if(Rand(0, 100) > baseKillPercentage * survivalChance)
		return false;

	return true;
}
// ALGORITHMS

void MapGenerator::FocusPointAlgorithm(Room& room, double baseKillPercentage, int imgID)
{
	if(room.h == 0 || room.w == 0)
	{
		Log->AddMessage("ERROR: FocusPointAlgorithm recieved a room with either zero width or zero height");
		return;
	}

	if(baseKillPercentage > 1.1)
		baseKillPercentage /= 100;

	baseKillPercentage = .4;

	// how much to add to each focus point
	int focusAddition = 50;
	int focusMinorAddition = 40;

	// total number of focus points
	int totalFocusPoints = (room.h * room.w) / 10.0;
	++totalFocusPoints; // ensure at least one focus point
	
	// create our room of focus points
	vector<vector<int> > map;
	map.reserve(room.h);
	map.resize(room.h);
	for(int h = 0; h < map.size(); ++h)
	{
		map.reserve(room.w);
		map[h].resize(room.w);
		for(int w = 0; w < map[h].size(); ++w)
			map[h][w] = 0; // make sure to set all points equal to zero focus
	}

	// iterate while we still have focus points to place
	while(totalFocusPoints--)
	{
		Vector2D at;
		at.x = Rand(0, room.w - 1);
		at.y = Rand(0, room.h - 1);
		
		map[at.y][at.x] = focusAddition;

		if(SafeLocation(map, at.y - 1, at.x))		map[at.y - 1][at.x]		+= focusMinorAddition;
		if(SafeLocation(map, at.y, at.x - 1))		map[at.y][at.x - 1]		+= focusMinorAddition;
		if(SafeLocation(map, at.y - 1, at.x - 1))	map[at.y - 1][at.x - 1] += focusMinorAddition;
		if(SafeLocation(map, at.y - 1, at.x + 1))	map[at.y - 1][at.x + 1] += focusMinorAddition;
		if(SafeLocation(map, at.y, at.x - 1))		map[at.y][at.x - 1]		+= focusMinorAddition;
		if(SafeLocation(map, at.y, at.x + 1))		map[at.y][at.x + 1]		+= focusMinorAddition;
		if(SafeLocation(map, at.y + 1, at.x - 1))	map[at.y + 1][at.x - 1] += focusMinorAddition;
		if(SafeLocation(map, at.y + 1, at.x + 1))	map[at.y + 1][at.x + 1] += focusMinorAddition;
	}

	// focus points have been placed, now we have to "kill" some tiles
	for(int h = 0; h < map.size(); ++h)
		for(int w = 0; w < map[h].size(); ++w)
			if(FocusPointAliveOrDead(baseKillPercentage, map[h][w])) // checks to see if this tile survives or not
				room.tiles[h][w][0] = imgID; // the tile survives

}

